]> dgit.raspbian.org Git - gnome-shell.git/commitdiff
edge-drag-gesture: Small improvements to gesture detection
authorJonas Dreßler <verdre@v0yd.nl>
Fri, 25 Jul 2025 10:31:36 +0000 (12:31 +0200)
committerCarlos Garnacho <carlosg@gnome.org>
Thu, 28 Aug 2025 17:14:06 +0000 (19:14 +0200)
Just a few small improvements to the accuracy of detection that came up
while porting to ClutterGesture.

- Use a fixed CANCEL_THRESHOLD instead of comparing the moved distance to
the perpendicular direction distance. Comparing to the perpendicular
distance means the point is basically allowed to travel in a max 45 degree
angle from the screen edge. Our EDGE_THRESHOLD is quite small though (20),
so after the gesture begins to recognize, it's surprisingly easy to
unwillingly cancel the gesture (just needs to move 20 pixels in the
perpendicular direction). Instead simply go with a fixed threshold for
cancelling the gesture. This has the additional upside that we'll also
move the state to CANCELLED if the gesture didn't move to RECOGNIZING
beforehand, a behavior that ClutterGesture strongly encourages.

- Introduce a cancel timeout of 200 ms, so that the gesture is cancelled
if it point is still hovering at the monitor edge for a while. The timeout
should be fairly short, as this gesture can happen on top of an application
window, and the application can't use the event until the gesture has moved
to CANCELLED, so we don't want to keep the event to ourselves for too
long.

Part-of: <https://gitlab.gnome.org/GNOME/gnome-shell/-/merge_requests/2853>

src/shell-edge-drag-gesture.c

index a2a84fd2f046c25fc078b4a6932538a0de8f719f..f4f973299bb1e75956579ee1da55d76b4db7f18e 100644 (file)
 
 #define EDGE_THRESHOLD 20
 #define DRAG_DISTANCE 80
+#define CANCEL_THRESHOLD 100
+#define CANCEL_TIMEOUT_MS 200
 
 struct _ShellEdgeDragGesture
 {
   ClutterGesture parent;
 
   StSide side;
+
+  unsigned int cancel_timeout_point;
+  guint cancel_timeout_id;
 };
 
 enum
@@ -129,11 +134,11 @@ exceeds_cancel_threshold (ShellEdgeDragGesture *self,
     {
     case ST_SIDE_LEFT:
     case ST_SIDE_RIGHT:
-      return distance_y > distance_x;
+      return distance_y > CANCEL_THRESHOLD;
 
     case ST_SIDE_TOP:
     case ST_SIDE_BOTTOM:
-      return distance_x > distance_y;
+      return distance_x > CANCEL_THRESHOLD;
     }
 
   g_assert_not_reached ();
@@ -189,6 +194,18 @@ shell_edge_drag_gesture_should_handle_sequence (ClutterGesture     *gesture,
   return FALSE;
 }
 
+static gboolean
+on_cancel_timeout (gpointer data)
+{
+  ShellEdgeDragGesture *self = data;
+
+  if (is_near_monitor_edge (self, self->cancel_timeout_point))
+    clutter_gesture_set_state (CLUTTER_GESTURE (self), CLUTTER_GESTURE_STATE_CANCELLED);
+
+  self->cancel_timeout_id = 0;
+  return G_SOURCE_REMOVE;
+}
+
 static void
 shell_edge_drag_gesture_point_began (ClutterGesture *gesture,
                                      unsigned int    point)
@@ -202,6 +219,11 @@ shell_edge_drag_gesture_point_began (ClutterGesture *gesture,
       clutter_gesture_set_state (gesture, CLUTTER_GESTURE_STATE_CANCELLED);
       return;
     }
+
+  self->cancel_timeout_point = point;
+
+  g_assert (self->cancel_timeout_id == 0);
+  self->cancel_timeout_id = g_timeout_add (CANCEL_TIMEOUT_MS, on_cancel_timeout, self);
 }
 
 static void
@@ -210,17 +232,14 @@ shell_edge_drag_gesture_point_moved (ClutterGesture *gesture,
 {
   ShellEdgeDragGesture *self = SHELL_EDGE_DRAG_GESTURE (gesture);
 
-  if (clutter_gesture_get_state (gesture) == CLUTTER_GESTURE_STATE_POSSIBLE &&
-      is_near_monitor_edge (self, point))
-    return;
-
   if (exceeds_cancel_threshold (self, point))
     {
       clutter_gesture_set_state (gesture, CLUTTER_GESTURE_STATE_CANCELLED);
       return;
     }
 
-  if (clutter_gesture_get_state (gesture) == CLUTTER_GESTURE_STATE_POSSIBLE)
+  if (clutter_gesture_get_state (gesture) == CLUTTER_GESTURE_STATE_POSSIBLE &&
+      !is_near_monitor_edge (self, point))
     clutter_gesture_set_state (gesture, CLUTTER_GESTURE_STATE_RECOGNIZING);
 
   if (clutter_gesture_get_state (gesture) == CLUTTER_GESTURE_STATE_RECOGNIZING)
@@ -263,6 +282,18 @@ shell_edge_drag_gesture_point_ended (ClutterGesture *gesture,
   clutter_gesture_set_state (gesture, CLUTTER_GESTURE_STATE_CANCELLED);
 }
 
+static void
+shell_edge_drag_gesture_state_changed (ClutterGesture      *gesture,
+                                       ClutterGestureState  old_state,
+                                       ClutterGestureState  new_state)
+{
+  ShellEdgeDragGesture *self = SHELL_EDGE_DRAG_GESTURE (gesture);
+
+  if (new_state == CLUTTER_GESTURE_STATE_CANCELLED ||
+      new_state == CLUTTER_GESTURE_STATE_COMPLETED)
+    g_clear_handle_id (&self->cancel_timeout_id, g_source_remove);
+}
+
 static void
 shell_edge_drag_gesture_set_property (GObject      *gobject,
                                       unsigned int  prop_id,
@@ -319,6 +350,7 @@ shell_edge_drag_gesture_class_init (ShellEdgeDragGestureClass *klass)
   gesture_class->point_began = shell_edge_drag_gesture_point_began;
   gesture_class->point_moved = shell_edge_drag_gesture_point_moved;
   gesture_class->point_ended = shell_edge_drag_gesture_point_ended;
+  gesture_class->state_changed = shell_edge_drag_gesture_state_changed;
 
   gobject_class->set_property = shell_edge_drag_gesture_set_property;
   gobject_class->get_property = shell_edge_drag_gesture_get_property;